package org.gridgain.internal.h2.vector;

import org.gridgain.internal.h2.store.fs.FileUtils;
import org.gridgain.internal.h2.util.DistanceFunctions;
import org.gridgain.internal.h2.vector.tx.MyTransaction;
import org.gridgain.internal.h2.vector.tx.MyTransactionMap;
import org.gridgain.internal.h2.vector.tx.MyTransactionStore;

import java.util.HashMap;
import java.util.HashSet;
import java.util.List;

public class MyVector {

    private HashMap<String, Object> config;
    private String path;
    private String tableName;
    private String fileName;
    private MyMVStore.Builder builder;
    private MyMVStore store;

    public MyMVStore getStore() {
        return store;
    }

        public MyVector(final String path, final String tableName) {
            this.tableName = tableName;
            this.path = path;

            builder = new MyMVStore.Builder();
            builder.fileName(path + "/" + tableName + "_vc.db");
            this.fileName = path + "/" + tableName + "_vc.db";
            //this.store = builder.open();
        }
//
//    public MyVector(final String tableName, final HashMap<String, Object> config)
//    {
//        this.tableName = tableName;
//        this.config = config;
//
//        builder = new MyMVStore.Builder(config);
//        builder.fileName(path + "/" + tableName + "_vc.db");
//        this.fileName = path + "/" + tableName + "_vc.db";
//        //this.store = builder.open();
//    }

    public MyVector(final String path, final String tableName, final HashMap<String, Object> config)
    {
        this.tableName = tableName;
        this.config = config;
        this.path = path;

        builder = new MyMVStore.Builder(config);
        builder.fileName(path + "/" + tableName + "_vc.db");
        this.fileName = path + "/" + tableName + "_vc.db";
        //this.store = builder.open();
    }

    public Double[] getValue(final Object nodeKey)
    {
        MyTransactionStore ts = new MyTransactionStore(store);
        ts.init();

        MyTransaction tx = ts.begin();
        MyTransactionMap<Object, Double[]> m = tx.openMap("vc_" + tableName);
        Double[] vs = m.get(nodeKey);
        tx.commit();
        ts.close();
        return vs;
    }

    private Object getMin(final Double[] vs, final List<Object> lst)
    {
        if (lst.size() == 2)
        {
            Double[] d1 = getValue(lst.get(0));
            Double[] d2 = getValue(lst.get(1));

            Double eu = DistanceFunctions.euclideanDistance(vs, d1);
            Double eu1 = DistanceFunctions.euclideanDistance(vs, d2);

            if (eu < eu1)
            {
                return lst.get(0);
            }
            return lst.get(1);
        }
        return null;
    }

    /**
     * 创建 vectorTable
     * */
    public void createVectorTable(Boolean isForce) throws Exception {
        if (FileUtils.exists(fileName))
        {
            if (isForce == true) {
                FileUtils.delete(fileName);
            }
            else {
                throw new Exception("已经存在不能新建向量表！");
            }
        }

        if (this.store == null)
        {
            this.store = builder.open();
        }

        if (this.store != null)
        {
            MyTransactionStore ts = new MyTransactionStore(store);
            ts.init();

            MyTransaction tx = ts.begin();
            tx.openMap("vc_" + tableName + "_eu");
            tx.openMap("vc_" + tableName + "_cos");
            tx.openMap("vc_" + tableName);
            tx.commit();
            ts.close();
        }
    }

    /**
     * 删除
     * */
    public boolean delVector(Object nodeKey)
    {
        boolean rs = false;

        if (this.store == null) {
            this.store = builder.open();
        }

        MyTransactionStore ts = new MyTransactionStore(store);
        ts.init();

        MyTransaction tx = ts.begin();
        MyTransactionMap<Double, HashSet<Object>> m_eu = tx.openMap("vc_" + tableName + "_eu");
        MyTransactionMap<Double, HashSet<Object>> m_cos = tx.openMap("vc_" + tableName + "_cos");
        MyTransactionMap<Object, Double[]> m = tx.openMap("vc_" + tableName);

        if (!m.containsKey(nodeKey))
        {
            try {
                Double[] old = m.get(nodeKey);
                Double eu = DistanceFunctions.euclideanDistance(MyUtil.getDs(old), old);
                if (m_eu.containsKey(eu)) {
                    HashSet<Object> set = m_eu.get(eu);
                    set.remove(nodeKey);
                    m_eu.replace(eu, set);
                }

                Double cos = DistanceFunctions.cosineDistance(MyUtil.getDsCos(old), old);
                if (m_cos.containsKey(cos)) {
                    HashSet<Object> set = m_cos.get(cos);
                    set.remove(nodeKey);
                    m_cos.replace(cos, set);
                }

                m.remove(nodeKey);

                tx.commit();
                rs = true;
            }
            catch (Exception e) {
                tx.rollback();
            } finally {
                ts.close();
            }
        }
        return rs;
    }

    /**
     * 插入
     * */
    public boolean insertVector(Object nodeKey, Double[] value) {
        boolean rs = false;

        if (this.store == null) {
            this.store = builder.open();
        }

        MyTransactionStore ts = new MyTransactionStore(store);
        ts.init();

        MyTransaction tx = ts.begin();
        MyTransactionMap<Double, HashSet<Object>> m_eu = tx.openMap("vc_" + tableName + "_eu");
        MyTransactionMap<Double, HashSet<Object>> m_cos = tx.openMap("vc_" + tableName + "_cos");
        MyTransactionMap<Object, Double[]> m = tx.openMap("vc_" + tableName);

        if (m.containsKey(nodeKey)) {
            try {
                Double[] old = m.get(nodeKey);
                Double eu = DistanceFunctions.euclideanDistance(MyUtil.getDs(old), old);
                if (m_eu.containsKey(eu)) {
                    HashSet<Object> set = m_eu.get(eu);
                    set.remove(nodeKey);
                    m_eu.replace(eu, set);
                }

                Double cos = DistanceFunctions.cosineDistance(MyUtil.getDsCos(old), old);
                if (m_cos.containsKey(cos)) {
                    HashSet<Object> set = m_cos.get(cos);
                    set.remove(nodeKey);
                    m_cos.replace(cos, set);
                }

                m.replace(nodeKey, value);
                Double eu1 = DistanceFunctions.euclideanDistance(MyUtil.getDs(value), value);
                if (m_eu.containsKey(eu1)) {
                    HashSet<Object> set = m_eu.get(eu1);
                    set.add(nodeKey);
                    m_eu.replace(eu1, set);
                } else {
                    HashSet<Object> set = new HashSet<>();
                    set.add(nodeKey);
                    m_eu.put(eu1, set);
                }

                Double cos1 = DistanceFunctions.cosineDistance(MyUtil.getDsCos(value), value);
                if (m_cos.containsKey(cos1)) {
                    HashSet<Object> set = m_cos.get(cos1);
                    set.add(nodeKey);
                    m_cos.replace(cos1, set);
                } else {
                    HashSet<Object> set = new HashSet<>();
                    set.add(nodeKey);
                    m_cos.put(cos1, set);
                }

                tx.commit();
                rs = true;
            } catch (Exception e) {
                tx.rollback();
            } finally {
                ts.close();
            }
        } else {
            try {
                m.put(nodeKey, value);
                Double eu = DistanceFunctions.euclideanDistance(MyUtil.getDs(value), value);
                if (m_eu.containsKey(eu)) {
                    HashSet<Object> set = m_eu.get(eu);
                    set.add(nodeKey);
                    m_eu.replace(eu, set);
                } else {
                    HashSet<Object> set = new HashSet<>();
                    set.add(nodeKey);
                    m_eu.put(eu, set);
                }

                Double cos = DistanceFunctions.cosineDistance(MyUtil.getDsCos(value), value);
                if (m_cos.containsKey(cos)) {
                    HashSet<Object> set = m_cos.get(cos);
                    set.add(nodeKey);
                    m_cos.replace(cos, set);
                } else {
                    HashSet<Object> set = new HashSet<>();
                    set.add(nodeKey);
                    m_cos.put(cos, set);
                }

                tx.commit();
                rs = true;
            } catch (Exception e) {
                tx.rollback();
            } finally {
                ts.close();
            }
        }

        return rs;
    }

    /**
     * 查询
     * */
    public List<Object> query(final Double[] vs, final int range)
    {
        if (this.store == null)
        {
            this.store = builder.open();
        }

        MyTransactionStore ts = new MyTransactionStore(store);
        ts.init();

        MyTransaction tx = ts.begin();
        MyTransactionMap<Double, HashSet<Object>> m_eu = tx.openMap("vc_" + tableName + "_eu");
        MyTransactionMap<Double, HashSet<Object>> m_cos = tx.openMap("vc_" + tableName + "_cos");

        MyPage p = m_eu.map.getRootPage();
        MyPage p1 = m_cos.map.getRootPage();
        List<Object> mySet = MyVectorUtil.getResult(p, p1, vs, range);
        if (mySet.size() - 2 >= 0) {
            List<Object> lst1 = mySet.subList(mySet.size() - 2, mySet.size());
            Object obj = getMin(vs, lst1);
            mySet = mySet.subList(0, mySet.size() - 2);
            mySet.add(obj);
        }

        ts.close();
        return mySet;
    }

}









































